#
# Creates the markup for displaying formulas
#
# 2009, Mike Widner <mikewidner@gmail.com>
#
########

package Fangcheng::LaTeX::Formula;

use 5.006001;
use strict;
use warnings;
use vars qw( $AUTOLOAD @ISA );

require Fangcheng::XY;

@ISA = qw( Fangcheng::LaTeX );

use Fangcheng::Matrix;

my $VERSION = '0.01';

sub execute {
	my ($self, $matrix, $calc, $step) = @_;
	if ( not defined $self->formula ) {
		$self->formula( Fangcheng::Matrix->new( $matrix->rows(), '' ) );		
	}
	$self->matrixVals( $matrix );
	$self->stage( $calc->getStage( $step ) );
	
	if ( $calc->getStage( $step ) <= 1 ) {
		$self->setNegatives( $step );
	}
	elsif ( $calc->getStage( $step ) == 2 ) {
		$self->crossMultiply( $step );
	}	
	elsif ( $calc->getStage( $step ) == 3 ) {
		$self->result( $calc );
	}
	return( $self );
}

# set the formula values for each matrix field
sub initMatrix {
	my ($self) = @_;
	my $nRows = $self->matrixVals->rows;	# so we don't call it over and over
	foreach my $row ( 1 .. $nRows ) {
		foreach my $col ( 1 .. $nRows ) {
			$self->formula->set( $row, $col, qq| 0 & | );
			if ( $col == $row ) {
				$self->formula->set( $row, $col, qq| k_{$row} & |);
			}
			elsif ( $col == $row + 1 ) {
				$self->formula->set( $row, $col, qq| l_{$row} & | );
			}
			elsif ( $col == 1 and $row == $nRows ) {
				$self->formula->set( $row, $col, qq| l_{$row} & | );
			}
		}
		$self->formula->set( $row, $self->matrixVals->columns, qq| b_{$row} \\\\ | );		
	}
	return( $self );
}

# handles the formula matrix for steps
sub setNegatives {
	my ($self, $step) = @_;
	my $nRows = $self->matrixVals->rows;
	if ( $step >= $nRows - 1 ) {
		return( $self );	# no work to do anymore
	}
	elsif ( $step == 0 ) {
		return( $self->initMatrix() );	# just create the starting matrix
	}
	my $str = $self->formula->get( $nRows, $step );
	$str =~ s/\s*&\s*//;
	$str .= "l_{$step}";	
	$str = $self->toggleNegative( $str );
	$self->formula->set( $nRows, $step + 1, $str . '&' );
	return( $self );
}

sub toggleNegative {
	my ($self, $str) = @_;
	my $neg = '-';
	if ( ( $str =~ s/^$neg// ) == 0 ) {
		$str = $neg . $str;
	}
	return( $str );
}

sub togglePlusMinus {
	my ($self, $str) = @_;
	my $neg = '-';
	my $plus = '+';
	if ( ( $str =~ s/^$neg/$plus/ ) == 0 ) {
		$str =~ s/^$plus/$neg/;
	}
	return( $str );
}

sub crossMultiply {
	my ($self, $step) = @_;
	
	my $str;
	my $nRows = $self->matrixVals->rows;
	my $i = 2 - ( $nRows - $step );		# increment through the rows
	
	if ( not defined $self->crossStr or $self->crossStr eq '') {
		$str = "k_{1} b_{$nRows} - l_{$nRows} b_{1}"; # start formula is static
		$self->crossStr( $str );
	}
	else {
		$str = '(' . $self->crossStr . ')';
		my $val = $self->formula->get( $nRows, $i );
		$val .= "b_{$i}";
		$val = $self->togglePlusMinus( $val );
		$str = '(' . $self->crossStr . ") k_{$i} $val";
		$self->crossStr( $str );
	}
	$self->crossVal( $self->matrixVals->get( $nRows, $self->matrixVals->columns ) );
}

sub result {
	my ($self, $calc) = @_;
	my $num = $self->crossStr;
	my $nRows = $self->matrixVals->rows;
	my $x = "x_{$nRows}";
	my ($k, $l);
	foreach my $i (1 .. $nRows ) {
		$k .= "k_{$i}";
		$l .= "l_{$i}";
	}
	my $den = "$k + $l";
	my $ans = $self->matrixVals->get( $nRows, $self->matrixVals->columns );
	if ( $ans =~ /(\d+)\s*\/\s*(\d+)/ ) {
		$ans = qq|\\frac{$1}{$2}|;
	}
	$self->resultStr( qq|$x = \\frac{$num}{$den} = $ans| );
}

# convert the formula matrix into a string for display
sub toString {
	my ($self) = @_;
	my $str = '';
	
	if ( defined $self->resultStr ) {
		return( $self->wrapString( $self->resultStr ) );
	}
	
	# if we're into the second stage, we return 2 strings
	# one for the value, one for the formula
	if ( defined $self->crossStr ) {
		my @return = ( $self->wrapString( $self->crossVal ), 
					   $self->wrapString( $self->crossStr ) );
		return( @return );
	}
	
	foreach my $row ( 1 .. $self->matrixVals->rows() ) {
		foreach my $col ( 1 .. $self->matrixVals->columns() ) {
			$str .= $self->formula->get( $row, $col );
		}
	}
	
	return( $self->wrapString($str) );	
}

sub wrapString {
	my ($self, $str) = @_;
	my ($head, $tail);
	$head .= '\\usepackage{euler}';
	$head .= '\\begin{equation*}';
	
	if ( not defined $self->crossStr ) {
		$head .= '\\begin{bmatrix}';
		$tail .= '\\end{bmatrix}';		
	}
	
	$tail .= '\\end{equation*}';
	return( $head . $str . $tail );
}

1;